<template>
  <div v-if="showSuccessFiles || status !== 'success'" class="uploader-file" :status="status">
    <slot
      :file="file"
      :list="list"
      :status="status"
      :paused="paused"
      :error="error"
      :response="response"
      :average-speed="averageSpeed"
      :formated-average-speed="formatedAverageSpeed"
      :current-speed="currentSpeed"
      :is-complete="isComplete"
      :is-uploading="isUploading"
      :size="size"
      :formated-size="formatedSize"
      :uploaded-size="uploadedSize"
      :progress="progress"
      :progress-style="progressStyle"
      :progressing-class="progressingClass"
      :time-remaining="timeRemaining"
      :formated-time-remaining="formatedTimeRemaining"
      :type="type"
      :extension="extension"
      :file-category="fileCategory"
    >
      <div class="uploader-file-progress" :class="progressingClass" :style="progressStyle" />
      <div class="uploader-file-info">
        <div class="uploader-file-name">
          <i class="uploader-file-icon" :icon="fileCategory" />
          {{ file.name }}
        </div>
        <div class="uploader-file-size">{{ formatedSize }}</div>
        <div class="uploader-file-meta" />
        <div class="uploader-file-status">
          <span v-show="status !== 'uploading'">{{ statusText }}</span>
          <span v-show="status === 'uploading'">
            <span>{{ progressStyle.progress }}</span>
            <em>{{ formatedAverageSpeed }}</em>
            <i>{{ formatedTimeRemaining }}</i>
          </span>
        </div>
        <div class="uploader-file-actions">
          <i class="el-icon-video-pause uploader-file-pause" @click="pause" />
          <i class="el-icon-video-play uploader-file-resume" @click="resume" />
          <i class="el-icon-refresh uploader-file-retry" @click="retry" />
          <i class="el-icon-close uploader-file-remove" @click="remove" />
          <!-- <i class="el-icon-download uploader-file-download" @click="download" />
          <i class="el-icon-delete uploader-file-delete" @click="deleteFile" />-->
        </div>
      </div>
    </slot>
  </div>
</template>

<script>
import Uploader from 'simple-uploader.js'
import events from '../common/file-events'
import { secondsToStr } from '../common/utils'
import { Message } from 'element-ui'
const COMPONENT_NAME = 'uploader-file'

export default {
  name: COMPONENT_NAME,
  props: {
    file: {
      type: Object,
      default() {
        return {}
      }
    },
    list: {
      type: Boolean,
      default: false
    },
    showSuccessFiles: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      response: null,
      paused: false,
      error: false,
      averageSpeed: 0,
      currentSpeed: 0,
      isComplete: false,
      isUploading: false,
      size: 0,
      formatedSize: '',
      uploadedSize: 0,
      progress: 0,
      timeRemaining: 0,
      type: '',
      extension: '',
      progressingClass: ''
    }
  },
  computed: {
    fileCategory() {
      const extension = this.extension
      const isFolder = this.file.isFolder
      let type = isFolder ? 'folder' : 'unknown'
      const categoryMap = this.file.uploader.opts.categoryMap
      const typeMap = categoryMap || {
        image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
        video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
        audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
        document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
      }
      Object.keys(typeMap).forEach((_type) => {
        const extensions = typeMap[_type]
        if (extensions.indexOf(extension) > -1) {
          type = _type
        }
      })
      return type
    },
    progressStyle() {
      const progress = Math.floor(this.progress * 100)
      const style = `translateX(${Math.floor(progress - 100)}%)`
      return {
        progress: `${progress}%`,
        webkitTransform: style,
        mozTransform: style,
        msTransform: style,
        transform: style
      }
    },
    formatedAverageSpeed() {
      return `${Uploader.utils.formatSize(this.averageSpeed)} / s`
    },
    status() {
      const isUploading = this.isUploading
      const isComplete = this.isComplete
      const isError = this.error
      const paused = this.paused
      if (isComplete) {
        return 'success'
      } else if (isError) {
        return 'error'
      } else if (isUploading) {
        return 'uploading'
      } else if (paused) {
        return 'paused'
      } else {
        return 'waiting'
      }
    },
    statusText() {
      const status = this.status
      const fileStatusText = this.file.uploader.fileStatusText
      let txt = status
      if (typeof fileStatusText === 'function') {
        txt = fileStatusText(status, this.response)
      } else {
        txt = fileStatusText[status]
      }
      return txt || status
    },
    formatedTimeRemaining() {
      const timeRemaining = this.timeRemaining
      const file = this.file
      if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
        return ''
      }
      let parsedTimeRemaining = secondsToStr(timeRemaining)
      const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
      if (parseTimeRemaining) {
        parsedTimeRemaining = parseTimeRemaining(timeRemaining, parsedTimeRemaining)
      }
      return parsedTimeRemaining
    }
  },
  watch: {
    status(newStatus, oldStatus) {
      if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
        this.tid = setTimeout(() => {
          this.progressingClass = 'uploader-file-progressing'
        }, 200)
      } else {
        clearTimeout(this.tid)
        this.progressingClass = ''
      }
    }
  },
  mounted() {
    const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed']
    const fnProps = [
      'isComplete',
      'isUploading',
      {
        key: 'size',
        fn: 'getSize'
      },
      {
        key: 'formatedSize',
        fn: 'getFormatSize'
      },
      {
        key: 'uploadedSize',
        fn: 'sizeUploaded'
      },
      'progress',
      'timeRemaining',
      {
        key: 'type',
        fn: 'getType'
      },
      {
        key: 'extension',
        fn: 'getExtension'
      }
    ]
    staticProps.forEach(prop => {
      this[prop] = this.file[prop]
    })
    fnProps.forEach((fnProp) => {
      if (typeof fnProp === 'string') {
        this[fnProp] = this.file[fnProp]()
      } else {
        this[fnProp.key] = this.file[fnProp.fn]()
      }
    })

    const handlers = this._handlers = {}
    const eventHandler = (event) => {
      handlers[event] = (...args) => {
        this.fileEventsHandler(event, args)
      }
      return handlers[event]
    }
    events.forEach((event) => {
      this.file.uploader.on(event, eventHandler(event))
    })
  },
  destroyed() {
    events.forEach((event) => {
      this.file.uploader.off(event, this._handlers[event])
    })
    this._handlers = null
  },
  methods: {
    _actionCheck() {
      this.paused = this.file.paused
      this.error = this.file.error
      this.isUploading = this.file.isUploading()
    },
    pause() {
      this.file.pause()
      this._actionCheck()
      this._fileProgress()
    },
    resume() {
      this.file.resume()
      this._actionCheck()
    },
    remove() {
      this.file.cancel()
    },
    retry() {
      this.file.retry()
      this._actionCheck()
    },
    download() {
      this.$emit('file-download', this.file)
    },
    deleteFile() {
      this.$emit('file-delete', this.file)
      this.file.cancel()
    },
    processResponse(message) {
      let res = message
      try {
        res = JSON.parse(message)
      } catch (e) {
        console.log(e)
      }
      this.response = res
    },
    fileEventsHandler(event, args) {
      const rootFile = args[0]
      const file = args[1]
      const target = this.list ? rootFile : file
      if (this.file === target) {
        if (this.list && event === 'fileSuccess') {
          this.processResponse(args[2])
          return
        }
        this[`_${event}`].apply(this, args)
      }
    },
    _fileProgress() {
      this.progress = this.file.progress()
      this.averageSpeed = this.file.averageSpeed
      this.currentSpeed = this.file.currentSpeed
      this.timeRemaining = this.file.timeRemaining()
      this.uploadedSize = this.file.sizeUploaded()
      this._actionCheck()
    },
    _fileSuccess(rootFile, file, message) {
      if (rootFile) {
        this.processResponse(message)
      }
      this._fileProgress()
      this.error = false
      this.isComplete = true
      this.isUploading = false
    },
    _fileComplete() {
      const failedChunks = this.file.chunks.filter(chunk => chunk.processedState.err !== null)
      if (failedChunks && failedChunks.length > 0) {
        this._fileError(null, null, failedChunks[0].processedState.res)
      } else {
        this._fileSuccess()
      }
    },
    _fileError(rootFile, file, data) {
      this._fileProgress()
      // 提示错误信息
      const result = JSON.parse(data)
      Message.error(result.message)
      this.processResponse(data)
      this.error = true
      this.isComplete = false
      this.isUploading = false
    }
  }
}
</script>

<style lang="scss" scoped>
.uploader-file {
  position: relative;
  height: 39px;
  line-height: 39px;
  overflow: hidden;
  border-top: 1px dashed #d9d9d9;
}
.uploader-file[status="waiting"] .uploader-file-pause,
.uploader-file[status="uploading"] .uploader-file-pause {
  display: block;
}
.uploader-file[status="paused"] .uploader-file-resume {
  display: block;
}
.uploader-file[status="error"] .uploader-file-retry {
  display: block;
}
.uploader-file[status="success"] .uploader-file-remove {
  display: none;
}
.uploader-file[status="success"] .uploader-file-download {
  display: block;
}
.uploader-file[status="success"] .uploader-file-delete {
  display: block;
}
.uploader-file[status="error"] .uploader-file-progress {
  background: #fee9ea;
}
.uploader-file-progress {
  position: absolute;
  width: 100%;
  height: 100%;
  background: #e8f4ff;
  transform: translateX(-100%);
}
.uploader-file-progressing {
  transition: all 0.4s linear;
}
.uploader-file-info {
  position: relative;
  z-index: 1;
  height: 100%;
  overflow: hidden;
}
.uploader-file-info:hover {
  background-color: rgba(240, 240, 240, 0.2);
}
.uploader-file-info i,
.uploader-file-info em {
  font-style: normal;
}
.uploader-file-name,
.uploader-file-size,
.uploader-file-meta,
.uploader-file-status,
.uploader-file-actions {
  float: left;
  position: relative;
  height: 100%;
}
.uploader-file-name {
  width: 45%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-indent: 14px;
}
.uploader-file-icon {
  display: inline-block;
  margin-right: 8px;
  height: 38px;
  line-height: 38px;
}
.uploader-file-icon::before {
  content: "📃";
  display: block;
  height: 100%;
  font-size: 24px;
  text-indent: 0;
  width: 24px;
  height: 24px;
}
.uploader-file-icon[icon="folder"]::before {
  content: "📂";
}
.uploader-file-icon[icon="image"]::before {
  content: "📊";
}
.uploader-file-icon[icon="video"]::before {
  content: "📹";
}
.uploader-file-icon[icon="audio"]::before {
  content: "🎵";
}
.uploader-file-icon[icon="document"]::before {
  content: "📋";
}
.uploader-file-size {
  width: 13%;
  text-indent: 10px;
}
.uploader-file-meta {
  width: 8%;
}
.uploader-file-status {
  width: 24%;
  text-indent: 20px;
}
.uploader-file-actions {
  width: 10%;
  text-align: center;
}
.uploader-file-actions > i {
  display: none;
  float: left;
  line-height: 38px;
  font-size: 16px;
  margin-right: 10px;
  cursor: pointer;
}
.uploader-file-actions > i:hover {
  color: #1890ff;
}
.uploader-file-actions .uploader-file-remove {
  display: block;
}
</style>
